ALV-Grid und Dropdown

Es gibt einige Verhaltensweise in SAP-Objekten, bei denen ich einfach nicht verstehe, warum diese nicht schon seit langer Zeit behoben wurden. Eine dieser Missstände ist, dass im Eingabebereiten ALV-Grid Felder nicht leer gelassen werden können, wenn an der Domäne Festwerte definiert sind und zu den Festwerten kein SPACE gehört. Dann sieht es so aus:

Ein anderes Phänomen sind für mich die Drop-Down-Listen, oder auch Listboxen oder Auswahllisten. Hier gibt es zwei Varianten:

  • Variante A: nur mit dem internen Feldwert
  • Variante B: Alias, die einen internen Feldwert und eine Bezeichnung erlaubt

Variante A ist fast immer nutzlos, denn in den seltensten Fällen möchte man nur den internen Wert in der Auswahlhilfe sehen. Hier ein Beispiel im Feld “Status”:

Variante B mit dem Alias ist bereits hilfreicher, denn der Anwender kann bei der Auswahl der Werte erkennen, was der Wert bedeutet. Hier ein Beispiel im Feld “Fruit”:

Hier ist für mich jedoch das Manko, dass nach Auswahl der Bezeichner nicht mehr zu sehen ist. Zudem wird der interne Wert in der Drop-Down-Liste nicht angezeigt, wenn man ihn nicht explizit mit einprogrammiert.

Beide Varianten unterscheiden sich somit von den Listboxen, die man vom Dynpro kennt. Hier kann man den beschreibenden Text nicht nur bei der Eingabe sehen:

Sondern auch nach der Auswahl:

Im SAPGUI kannst du noch einstellen, ob du den Schlüsselwert sehen möchtest oder nicht:

Das ist eine sinnvolle Darstellung von Listboxen.

Drop-Down-Liste und Festwerte

Für das ALV-Grid gibt es meines Wissens keine einfache Alternative um ein Feld als Listbox im Feldkatalog zu definieren, so dass vorhandene Festwerte direkt und ohne weiteres Zutun in der Drop-Down-Liste angezeigt werden.

Alternativen

Jedes Mal auf’s Neue kann ich nicht glaube, dass es im ALV-Grid keine andere Möglichkeit gibt, also habe ich mir folgende Alternativen überlegt. Beide Alternativen arbeiten mit einem zusätzlichen Feld.

  1. Auswahl Schlüsselfeld und Beschreibung in separates Feld
  2. Auswahl Textfeld und Ermittlung der ID

Beide Varianten bedeuten mehr Aufwand, denn du musst das Ereignis DATA_CHANGED registrieren und ausprogrammieren. Sie bedeuten jedoch für den Anwender auch einen größeren Bedienkomfort.

Leider habe ich noch keine Möglichkeit gefunden, dass der Text sofort nach Auswahl aktualisiert wird. Der Anwender muss in jedem Fall eine Taste drücken oder in ein anderes Feld klicken.

Auswahl Schlüsselfeld

Ich definiere für die Ausgabe ein separates Feld, das den Bezeichner des zugehörigen Feldes aufnimmt. In diesem Beispiel das Feld “Hardware-ID”:

Ich verwende die Drop-Down-Alias-Variante. Zusätzlich wird im Ereignis CL_GUI_ALV_GRID-DATA_CHANGED der Feldwert ausgelesen und mittels MODIFY_CELL in das zugehöriger Bezeichnerfeld geschrieben.

Auswahl Textfeld

In dieser Variante definiere ich die Drop-Drown-Liste für das Beschreibungsfeld und ermittle aus der Beschreibung den internen Schlüssel:

Fazit

Welche Variante du verwendest, musst du selbst entscheiden. Meiner Meinung nach ist die erste Variante einen Tick einfacher, da hier bei dem internen Feldwert der richtige interne Wert direkt mitgegeben werden kann und so der Text zur ID ermittelt wird. Bei der zweiten Variante wird die ID zum Text ermittelt, was eher ungewöhnlich ist.

Vorteil der zweiten Variante ist, dass das Schlüsselfeld mit dem internen Wert einfach versteckt werden kann und so für den Benutzer gar nicht sichtbar ist. Der Benutzer sieht also nur den Wert, der in der Regel auch sprechend und sinnvoll für ihn ist.

In beiden Fällen muss sichergestellt werden, dass interner Wert und externe Beschreibung immer zueinander passen und korrekt ermittelt werden.

Code

REPORT.

PARAMETERS p_hwid TYPE c LENGTH 1 AS LISTBOX VISIBLE LENGTH 20.


CLASS main DEFINITION.
  PUBLIC SECTION.
    TYPES: BEGIN OF _line,
             id   TYPE c LENGTH 1,
             text TYPE c LENGTH 20,
           END OF _line,
           _tab TYPE SORTED TABLE OF _line WITH UNIQUE KEY id.

    TYPES: BEGIN OF _data_line,
             key         TYPE c LENGTH 2,
             status      TYPE c LENGTH 1, "Dropdown
             fruit       TYPE c LENGTH 1, "Dropdown Alias
             hardware    TYPE c LENGTH 1, "Dropdown Alias + separate text field
             hw_text     TYPE c LENGTH 20,
             weekday_id  TYPE c LENGTH 1,
             weekday_txt TYPE c LENGTH 20,
           END OF _data_line,
           _data_table TYPE STANDARD TABLE OF _data_line WITH EMPTY KEY.

    CLASS-DATA hw_ids TYPE _tab.
    CLASS-DATA weekday_ids TYPE _tab.

    METHODS constructor.
    CLASS-METHODS class_constructor.

    METHODS display
      IMPORTING
        container TYPE REF TO cl_gui_container.

  PRIVATE SECTION.
    DATA datatable TYPE _data_table.
    DATA grid TYPE REF TO cl_gui_alv_grid.

    DATA dropdown       TYPE lvc_t_drop.
    DATA dropdown_alias TYPE lvc_t_dral.

    CONSTANTS dd_handle_status  TYPE i VALUE 1.
    CONSTANTS dd_handle_fruit   TYPE i VALUE 2.
    CONSTANTS dd_handle_hw      TYPE i VALUE 3.
    CONSTANTS dd_handle_weekday TYPE i VALUE 4.

    METHODS on_data_changed FOR EVENT data_changed
      OF cl_gui_alv_grid
      IMPORTING
        e_onf4
        e_onf4_after
        e_onf4_before
        e_ucomm
        er_data_changed.

    METHODS get_fcat
      RETURNING VALUE(result) TYPE lvc_t_fcat.
ENDCLASS.

CLASS main IMPLEMENTATION.
  METHOD class_constructor.

    hw_ids = VALUE #(
      ( id = 'X' text = 'Tablet' )
      ( id = 'Y' text = 'Personal Computer' )
      ( id = 'Z' text = 'Laptop' ) ).

    weekday_ids = VALUE #(
      ( id = '1' text = 'Monday' )
      ( id = '2' text = 'Tuesday' )
      ( id = '3' text = 'Wednesday' )
      ( id = '4' text = 'Thursday' )
      ( id = '5' text = 'Friday' )
      ( id = '6' text = 'Saturday' )
      ( id = '7' text = 'Sunday' ) ).

  ENDMETHOD.

  METHOD constructor.

    "Simple Dropdown: STATUS
    dropdown = VALUE #( handle = dd_handle_status
      ( value = '1' )
      ( value = '2' )
      ( value = '3' ) ).

    "Dropdown-Alias: FRUITS
    dropdown_alias = VALUE #( handle = dd_handle_fruit
      ( int_value = 'A' value = 'Apple' )
      ( int_value = 'B' value = 'Banana' )
      ( int_value = 'C' value = 'Coconut' ) ).

    "Dropdown-Alias with description field: HARDWARE
    APPEND LINES OF VALUE lvc_t_dral(
      FOR l_hw IN hw_ids (
        handle = dd_handle_hw
        int_value = l_hw-id
        value = l_hw-text ) )
          TO dropdown_alias.

    "Dropdown-Alias with description selection: WEEKDAY
    APPEND LINES OF VALUE lvc_t_dral(
      FOR l_wd IN weekday_ids (
        handle = dd_handle_weekday
        int_value = l_wd-text
        value = l_wd-text ) )
          TO dropdown_alias.

    "fill demo data in internal table
    datatable = VALUE #(
       ( key = '10' status = '1' fruit = 'A' hardware = 'X' weekday_id = '3' )
       ( key = '20' )
       ( key = '30' ) ).

  ENDMETHOD.

  METHOD display.

    grid = NEW #( i_parent = container ).

    DATA(fcat) = get_fcat( ).

    LOOP AT datatable ASSIGNING FIELD-SYMBOL(<data>).
      IF <data>-hardware IS NOT INITIAL.
        <data>-hw_text = dropdown_alias[ handle = dd_handle_hw int_value = <data>-hardware ]-value.
        <data>-weekday_txt = weekday_ids[ id = <data>-weekday_id ]-text.
      ENDIF.
    ENDLOOP.

    grid->set_drop_down_table(
      it_drop_down       = dropdown
      it_drop_down_alias = dropdown_alias ).

    grid->set_table_for_first_display(
      CHANGING
        it_outtab                     = datatable
        it_fieldcatalog               = fcat
      EXCEPTIONS
        OTHERS                        = 4 ).
    IF sy-subrc > 0.
      RETURN.
    ENDIF.

    SET HANDLER on_data_changed FOR grid.
    grid->register_edit_event( cl_gui_alv_grid=>mc_evt_modified ).

  ENDMETHOD.

  METHOD get_fcat.

      result = VALUE #( tabname = '1' datatype = 'CHAR'
      ( fieldname = 'KEY'         outputlen = 10 intlen =  2 drdn_hndl = 0                 drdn_alias = ' ' reptext = 'Key'             edit = ' ' )
      ( fieldname = 'STATUS'      outputlen = 10 intlen =  1 drdn_hndl = dd_handle_status  drdn_alias = ' ' reptext = 'Status'          edit = 'X' )
      ( fieldname = 'FRUIT'       outputlen = 10 intlen =  1 drdn_hndl = dd_handle_fruit   drdn_alias = 'X' reptext = 'Fruit'           edit = 'X' )
      ( fieldname = 'HARDWARE'    outputlen = 10 intlen =  1 drdn_hndl = dd_handle_hw      drdn_alias = 'X' reptext = 'Hardware-ID'     edit = 'X' )
      ( fieldname = 'HW_TEXT'     outputlen = 30 intlen = 20 drdn_hndl = 0                 drdn_alias = ' ' reptext = 'Hardware descr.' edit = ' ' )
      ( fieldname = 'WEEKDAY_ID'  outputlen = 10 intlen =  1 drdn_hndl = 0                 drdn_alias = ' ' reptext = 'Weekday ID'      edit = ' ' )
      ( fieldname = 'WEEKDAY_TXT' outputlen = 20 intlen = 20 drdn_hndl = dd_handle_weekday drdn_alias = 'X' reptext = 'Weekday'         edit = 'X' lowercase = 'X')
        ).

  ENDMETHOD.

  METHOD on_data_changed.
    DATA text TYPE string.

    LOOP AT er_data_changed->mt_good_cells INTO DATA(cell).
      CASE cell-fieldname.
        WHEN 'HARDWARE'.
          text = COND #( WHEN cell-value IS INITIAL THEN `` ELSE dropdown_alias[ handle = dd_handle_hw int_value = cell-value ]-value ).
          er_data_changed->modify_cell(
            i_row_id    = cell-row_id
            i_tabix     = cell-tabix
            i_fieldname = 'HW_TEXT'
            i_value     = text ).
        WHEN 'WEEKDAY_TXT'.
          text = COND #( WHEN cell-value IS INITIAL THEN `` ELSE weekday_ids[ text = cell-value ]-id ).
          er_data_changed->modify_cell(
            i_row_id    = cell-row_id
            i_tabix     = cell-tabix
            i_fieldname = 'WEEKDAY_ID'
            i_value     = text ).
      ENDCASE.
    ENDLOOP.
  ENDMETHOD.
ENDCLASS.


INITIALIZATION.
  DATA(docker) = NEW cl_gui_docking_container( side = cl_gui_docking_container=>dock_at_bottom ratio = 80 ).
  DATA(app) = NEW main( ).
  app->display( docker ).

  CALL FUNCTION 'VRM_SET_VALUES'
    EXPORTING
      id     = 'P_HWID'
      values = CORRESPONDING vrm_values( main=>hw_ids MAPPING key = id text = text ).
Enno Wulff